/*
 * DemoTableModel.java
 * 
 * Written by Joseph Bowbeer and released to the public domain,
 * as explained at http://creativecommons.org/licenses/publicdomain
 */

package jozart.remotetabledemo;

import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;

/**
 * Defines the column structure and other metadata shared by the demo server and
 * its clients. Provides utility methods for creating a local client instance
 * and a remote server instance.
 * <p>
 * The model structure and data were extracted from <i>The Java Tutorial</i>.
 * 
 * @author Joseph Bowbeer
 * @version 1.0
 */
public class DemoTableModel extends DefaultTableModel {

	/** Column names. */
    private static final String[] NAMES = {
        "First Name", "Last Name", "Sport", "# of Years", "Vegetarian"
    };

    /** Column classes. */
    private static final Class[] CLASSES = new Class[] {
        String.class, String.class, String.class, Integer.class, Boolean.class
    };

    /** Default contents. */
    private static final Object[][] DEFAULT_DATA = {
        { "Mary", "Campione", "Snowboarding", new Integer(5), Boolean.FALSE },
        { "Alison", "Huml", "Rowing", new Integer(3), Boolean.TRUE },
        { "Kathy", "Walrath", "Chasing toddlers", new Integer(2), Boolean.FALSE },
        { "Mark", "Andrews", "Speed reading", new Integer(20), Boolean.TRUE },
        { "Angela", "Lih", "Teaching high school", new Integer(4), Boolean.FALSE }
    };

    /** Creates an empty model. */
    public DemoTableModel() {
        super(NAMES, 0);
    }

    /** Creates a model with the given data. */
    public DemoTableModel(Object[][] data) {
        super(data, NAMES);
    }

    /**
     * Returns the most specific superclass for all the cell values
     * in the column.
     */
    public Class<?> getColumnClass(int columnIndex) {
        return CLASSES[columnIndex];
    }

    /**
     * Returns true if the cell at <tt>rowIndex</tt> and <tt>columnIndex</tt>
     * is editable.
     * <p>
     * In this implementation, all but the first two columns are editable.
     */
    public boolean isCellEditable(int rowIndex, int columnIndex) {
		return (columnIndex > 1);
	}

    /**
     * Creates empty table model for client.
     * <p>
     * Note: Although the server model provides the row data,
     * the client and server models must agree on their column
     * structure.
     *
     * @param editable indicates if this model can be edited
     * @return client table model
     */
    public static DefaultTableModel createClientModel(final boolean editable) {
        return new DemoTableModel() {
            public boolean isCellEditable(int rowIndex, int columnIndex) {
                return editable && super.isCellEditable(rowIndex, columnIndex);
            }
        };
    }

    /**
     * Creates table model for server with data from <i>The Java Tutorial</i>.
     * <p>
     * This implementation simulates slow reads and even slower writes.
     * 
     * @return server table model
     */
    public static TableModel createServerModel() {
        return createSlowServerModel(DEFAULT_DATA, 100, 1000);
    }

    /**
     * Creates table model with specified data and simulated delays.
     *
     * @param data the data for the table
     * @param readDelay millisecond delay per read 
     * @param writeDelay millisecond delay per write
     * @return table model
     */
    private static TableModel createSlowServerModel(Object[][] data,
            final int readDelay, final int writeDelay) {
        return new DemoTableModel(data) {

            /** Sleeps <tt>millis</tt> msecs, postponing interrupts. */
            private void delay(int millis) {
                try {
                    if (millis != 0)
                        Thread.sleep(millis);
                } catch (InterruptedException ex) {
                    Thread.currentThread().interrupt();
                }
            }

            /** Reads with simulated delay. */
            public Object getValueAt(int rowIndex, int columnIndex) {
                delay(readDelay);
                return super.getValueAt(rowIndex, columnIndex);
            }

            /** Writes with simulated delay. */
            public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
                delay(writeDelay);
                super.setValueAt(aValue, rowIndex, columnIndex);
            }
        };
    }
}
